/*
 * WebQueryInstance.java
 *
 * Created on March 16, 2005, 9:23 PM
 */
package web;


import driver.AppLog;
import http.HTTPHelper;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import xml.XMLHelper;


/**
 *
 * @author Keith
 */
public class WebQueryInstance implements Runnable {

  static private int ID = 100;
  final private int id = ID++;
  private boolean querySuccess;
  private Thread myThread;
  /**
   * Holds value of property query.
   */
  final private WebQuery query;
  final private Properties params;
  /**
   * Holds value of property url.
   */
  private Set<URL> url;
  private Queue<URL> processQ = new LinkedList<URL>();
  private URL querySource;
  /**
   * Holds value of property queryResult.
   */
  private Document queryResult;
  private Logger myLogger = AppLog.appLog;


  /** Creates a new instance of WebQueryInstance
   * @param params 
   * @param webQuery 
   * @param maxRetryCnt 
   * @throws MalformedURLException
   */
  public WebQueryInstance( Properties params, WebQuery webQuery, int maxRetryCnt )
    throws MalformedURLException {
    this.params = params;
    this.query = webQuery;

    url = this.query.getURL( this.params );
    for ( int idx = 0; idx < maxRetryCnt; idx++ ) {
      for ( URL u : url ) {
        processQ.add( u );
      }
    }
  }


  /**
   * Getter for property url.
   * @return Value of property url.
   */
  public Set<URL> getUrl() {
    return url;
  }


  public Document getXmlGenerator() {
    return query.getXmlGenerator();
  }


  /**
   * Getter for property query.
   * @return Value of property query.
   */
  public WebQuery getQuery() {
    return this.query;
  }


  /**
   * Getter for property stock.
   * @return Value of property stock.
   */
  public Properties getParams() {
    return this.params;
  }


  public boolean isQuerySuccess() {
    return querySuccess;
  }


  /**
   * Getter for property queryResult.
   * @return Value of property queryResult.
   */
  public Document getQueryResult() {
    return queryResult;
  }


  protected Document applyStylesheet( Document xslDoc, Document xhtmlDoc )
    throws TransformerException {
    DOMResult domResult = new DOMResult();

    Transformer transformer = XMLHelper.getTransformer( xslDoc );

    transformer.clearParameters();
    if ( params != null ) {
      for ( String paramName : params.stringPropertyNames() ) {
        transformer.setParameter( paramName, params.getProperty( paramName ) );
      }
    }
    transformer.transform( new DOMSource( xhtmlDoc ), domResult );

    return (Document) domResult.getNode();
  }


  protected boolean isValidElement( Node n ) {
    boolean result;

//    System.out.println( "nodeName: " + n.getNodeName() + ", val: " + n.getNodeValue() );
    if ( n.getNodeType() == Node.TEXT_NODE ) {
      String val = n.getNodeValue();
      result = val != null && val.trim().length() > 0;
//      System.out.println( "result: " + result );
    } else {
      NodeList nl = n.getChildNodes();
      boolean allValid = true;

      for ( int idx = 0, len = nl.getLength(); allValid && idx < len; idx++ ) {
        Node child = nl.item( idx );

        allValid = isValidElement( child );
      }
      result = allValid;
    }

    return result;
  }


  protected boolean isValidDocument( Document doc ) {
    boolean result = false;

//    XMLHelper.dump( doc );

    // look for nodes that are empty
    Element root = (Element) doc.getDocumentElement();
    if ( root != null ) {
      NodeList nl = root.getChildNodes();
      if ( nl != null ) {
        int len = nl.getLength();
        if ( len > 0 ) {
          boolean allValid = true;
          for ( int idx = 0; allValid && idx < len; idx++ ) {
            Node n = nl.item( idx );

            allValid = isValidElement( n );
          }

          result = allValid;
        }
      }
    }

    return result;
  }


  public boolean isEmptyProcessQueue() {
    return processQ.isEmpty();
  }


  public void run() {
    myThread = Thread.currentThread();
    try {
      URL address = processQ.poll();

      if ( address != null ) {
        Document doc;

        myLogger.log( Level.INFO, "retrieving: " + address.getPath() );
        synchronized ( HTTPHelper.instance() ) {
          doc = HTTPHelper.getPage( address );

          myLogger.log( Level.FINE, "getPage1:" );
//        XMLHelper.dump(doc);
          NodeList metaNodes = doc.getElementsByTagName( "meta" );
          for ( int idx = 0, len = metaNodes.getLength(); idx < len; idx++ ) {
            Element meta = (Element) metaNodes.item( idx );
            String httpEquiv = meta.getAttribute( "http-equiv" );
            String content = meta.getAttribute( "content" );
            if ( "refresh".equalsIgnoreCase( httpEquiv ) &&
              content != null ) {
              myLogger.log( Level.FINE, "new loc: " + content );
              final String urlStart = "url='";
              int start = content.indexOf( urlStart );
              int end = content.lastIndexOf( '\'' );
              content = content.substring( start + urlStart.length(), end );
              myLogger.log( Level.FINE, "new loc: " + content );
              address = new URL( content );
              doc = HTTPHelper.getPage( address );
              myLogger.log( Level.FINE, "getPage2:" );
            //          XMLHelper.dump(doc);
            }
          }
          doc = applyStylesheet( getXmlGenerator(), doc );
          myLogger.log( Level.FINE, "applyStylesheet:" );
        }
        if ( this.query.getOutput() != null ) {
          final File f = query.getOutput();

          XMLHelper.dump( f, doc );
        }
        if ( isValidDocument( doc ) ) {
          querySuccess = true;
          queryResult = doc;
          querySource = address;
        } else {
          myLogger.log( Level.SEVERE, "Invalid document!" );
        }
      }
    } catch ( Throwable e ) {
      myLogger.log( Level.SEVERE, "Error encountered:", e );
//      e.printStackTrace();
    }
  }


  public void interrupt() {
    if ( myThread != null ) {
      myThread.interrupt();
    }
  }


  public URL getQuerySource() {
    return querySource;
  }
}
